﻿// License: https://opensource.org/licenses/MIT
// Create Date : 2018-01-01

using System;
using System.Diagnostics;
using Cat.Extensions;

namespace Cat.Core
{
    /// <summary>
    /// Cat.Core.Aspect 的扩展方法 : XLog日志记录
    /// </summary>
    public static class AspectXLogEx
    {
        /// <summary>
        /// 根据context.CallerInfo.CallerType的类型获取ILogger对象
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public static ILogger GetLogger(this IAspectContext context)
        {
            var callerType = context.CallerInfo?.CallerType;
            if (callerType != null) return Log.Get(callerType);
            return Log.Default;
        }
        /// <summary>
        /// 将 context.CallerInfo 的信息记录到日志中
        /// </summary>
        /// <param name="context"></param>
        /// <param name="includeArguments">null:自动 false:不包含参数 true:包含参数</param>
        /// <param name="level"></param>
        /// <param name="logger"></param>
        public static void LogCallerInfo(this IAspectContext context,
            bool? includeArguments, LogLevel level, ILogger logger = null)
        {
            LogCallerInfo(context, includeArguments, null, level, logger);
        }

        /// <summary>
        /// 将 context.CallerInfo 的信息记录到日志中
        /// </summary>
        /// <param name="context"></param>
        /// <param name="includeArguments">null:自动 false:不包含参数 true:包含参数</param>
        /// <param name="exc"></param>
        /// <param name="level"></param>
        /// <param name="logger"></param>
        public static void LogCallerInfo(this IAspectContext context,
            bool? includeArguments, Exception exc, LogLevel level, ILogger logger = null)
        {
            if (context.CallerInfo == null) return;
            var message = context.CallerInfo.GetCallerString(includeArguments);
            var logger0 = logger ?? GetLogger(context);
            switch (level)
            {
                case LogLevel.Debug: logger0.D(exc, message); break;
                case LogLevel.Info: logger0.I(exc, message); break;
                case LogLevel.Warn: logger0.W(exc, message); break;
                case LogLevel.Error: logger0.E(exc, message); break;
                case LogLevel.Fatal: logger0.F(exc, message); break;
            }
        }

        /// <summary>
        /// 记录异常日志
        /// </summary>
        /// <param name="context"></param>
        /// <param name="exc"></param>
        /// <param name="level"></param>
        /// <param name="logger"></param>
        public static void LogExc(this IAspectContext context, Exception exc,
            LogLevel level, ILogger logger = null)
        {
            var logger0 = logger ?? GetLogger(context);
            switch (level)
            {
                case LogLevel.Debug: logger0.D(exc); break;
                case LogLevel.Info: logger0.I(exc); break;
                case LogLevel.Warn: logger0.W(exc); break;
                case LogLevel.Error: logger0.E(exc); break;
                case LogLevel.Fatal: logger0.F(exc); break;
            }
        }

        #region LogCallerInfo

        /// <summary>
        /// 将 context.CallerInfo 的信息记录到日志中
        /// LogLevel.Debug
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="includeArguments">null:自动 false:不包含参数 true:包含参数</param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogCallerInfo(this Aspect aspect,
            bool? includeArguments, ILogger logger = null)
        {
            return LogCallerInfo(aspect, includeArguments, LogLevel.Debug, logger);
        }

        /// <summary>
        /// 将 context.CallerInfo 的信息记录到日志中
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="includeArguments">null:自动 false:不包含参数 true:包含参数</param>
        /// <param name="level"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogCallerInfo(this Aspect aspect,
            bool? includeArguments, LogLevel level, ILogger logger = null)
        {
            aspect.LoadCallerInfo();
            return aspect.Combine(context =>
            {
                LogCallerInfo(context, includeArguments, level, logger);
                context.DoWork();
            });
        }
        #endregion

        #region LogExcThrow
        /// <summary>
        /// 记录异常，且抛出异常
        ///  LogLevel.Error
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogExcThrow(this Aspect aspect, ILogger logger = null)
        {
            return LogExcThrow(aspect, LogLevel.Error, logger);
        }
        /// <summary>
        /// 记录异常，且抛出异常
        ///  LogLevel.Error
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogExcThrow<TExc>(this Aspect aspect, ILogger logger = null) where TExc : Exception
        {
            return LogExcThrow<TExc>(aspect, LogLevel.Error, logger);
        }
        /// <summary>
        /// 记录异常，且抛出异常
        /// </summary>
        public static Aspect LogExcThrow(this Aspect aspect,
            LogLevel level, ILogger logger = null)
        {
            return LogExcThrow<Exception>(aspect, level, logger);
        }
        /// <summary>
        /// 记录异常，且抛出异常
        /// </summary>
        public static Aspect LogExcThrow<TExc>(this Aspect aspect,
            LogLevel level, ILogger logger = null) where TExc : Exception
        {
            return aspect.Combine(context =>
            {
                try
                {
                    context.DoWork();
                }
                catch (TExc exc)
                {
                    var logger0 = logger ?? GetLogger(context);
                    switch (level)
                    {
                        case LogLevel.Debug: logger0.D(exc); break;
                        case LogLevel.Info: logger0.I(exc); break;
                        case LogLevel.Warn: logger0.W(exc); break;
                        case LogLevel.Error: logger0.E(exc); break;
                        case LogLevel.Fatal: logger0.F(exc); break;
                    }
                    throw;
                }
            });
        }
        #endregion

        #region LogExc
        /// <summary>
        /// 记录异常，不抛出异常
        /// LogLevel.Warn
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogExc(this Aspect aspect, ILogger logger = null)
        {
            return LogExc(aspect, LogLevel.Warn, logger);
        }
        /// <summary>
        /// 记录异常，不抛出异常
        /// LogLevel.Warn
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogExc<TExc>(this Aspect aspect, ILogger logger = null) where TExc : Exception
        {
            return LogExc<TExc>(aspect, LogLevel.Warn, logger);
        }
        /// <summary>
        /// 记录异常，不抛出异常
        /// </summary>
        public static Aspect LogExc(this Aspect aspect,
            LogLevel level, ILogger logger = null)
        {
            return LogExc<Exception>(aspect, level, logger);
        }
        /// <summary>
        /// 记录异常，不抛出异常
        /// </summary>
        public static Aspect LogExc<TExc>(this Aspect aspect,
            LogLevel level, ILogger logger = null) where TExc : Exception
        {
            return aspect.Combine(context =>
            {
                try
                {
                    context.DoWork();
                }
                catch (TExc exc)
                {
                    var logger0 = logger ?? GetLogger(context);
                    switch (level)
                    {
                        case LogLevel.Debug: logger0.D(exc); break;
                        case LogLevel.Info: logger0.I(exc); break;
                        case LogLevel.Warn: logger0.W(exc); break;
                        case LogLevel.Error: logger0.E(exc); break;
                        case LogLevel.Fatal: logger0.F(exc); break;
                    }
                }
            });
        }
        #endregion

        #region LogExcMoreThrow(发生异常时会记录CallerInfo)
        /// <summary>
        /// 记录异常和CallerInfo，且抛出异常
        /// LogLevel.Error
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogExcMoreThrow(this Aspect aspect, ILogger logger = null)
        {
            return LogExcMoreThrow(aspect, LogLevel.Error, logger);
        }
        /// <summary>
        /// 记录异常和CallerInfo，且抛出异常
        /// LogLevel.Error
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogExcMoreThrow<TExc>(this Aspect aspect, ILogger logger = null) where TExc : Exception
        {
            return LogExcMoreThrow<TExc>(aspect, LogLevel.Error, logger);
        }
        /// <summary>
        /// 记录异常和CallerInfo，且抛出异常
        /// </summary>
        public static Aspect LogExcMoreThrow(this Aspect aspect,
            LogLevel level, ILogger logger = null)
        {
            return LogExcMoreThrow<Exception>(aspect, level, logger);
        }
        /// <summary>
        /// 记录异常和CallerInfo，且抛出异常
        /// </summary>
        public static Aspect LogExcMoreThrow<TExc>(this Aspect aspect,
            LogLevel level, ILogger logger = null) where TExc : Exception
        {
            aspect.LoadCallerInfo();
            return aspect.Combine(context =>
            {
                try
                {
                    context.DoWork();
                }
                catch (TExc exc)
                {
                    LogCallerInfo(context, true, exc, level, logger);
                    throw;
                }
            });
        }
        #endregion

        #region LogExcMore(发生异常时会记录CallerInfo)
        /// <summary>
        /// 记录异常和CallerInfo，不抛出异常
        /// LogLevel.Warn
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogExcMore(this Aspect aspect, ILogger logger = null)
        {
            return LogExcMore(aspect, LogLevel.Warn, logger);
        }
        /// <summary>
        /// 记录异常和CallerInfo，不抛出异常
        /// LogLevel.Warn
        /// </summary>
        /// <typeparam name="TExc"></typeparam>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogExcMore<TExc>(this Aspect aspect, ILogger logger = null) where TExc : Exception
        {
            return LogExcMore<TExc>(aspect, LogLevel.Warn, logger);
        }
        /// <summary>
        /// 记录异常和CallerInfo，不抛出异常
        /// </summary>
        public static Aspect LogExcMore(this Aspect aspect,
            LogLevel level, ILogger logger = null)
        {
            return LogExcMore<Exception>(aspect, level, logger);
        }
        /// <summary>
        /// 记录异常和CallerInfo，不抛出异常
        /// </summary>
        public static Aspect LogExcMore<TExc>(this Aspect aspect,
            LogLevel level, ILogger logger = null) where TExc : Exception
        {
            aspect.LoadCallerInfo();
            return aspect.Combine(context =>
            {
                try
                {
                    context.DoWork();
                }
                catch (TExc exc)
                {
                    LogCallerInfo(context, true, exc, level, logger);
                }
            });
        }
        #endregion

        #region LogTimeCost

        /// <summary>
        /// 统计耗时并记录日志
        /// LogLevel.Info
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogTimeCost(this Aspect aspect, ILogger logger = null)
        {
            return LogTimeCost(aspect, null, LogLevel.Info, logger);
        }

        /// <summary>
        /// 统计耗时并记录日志
        /// </summary>
        public static Aspect LogTimeCost(this Aspect aspect,
            LogLevel level, ILogger logger = null)
        {
            return LogTimeCost(aspect, null, level, logger);
        }

        /// <summary>
        /// 统计耗时并记录日志
        /// </summary>
        /// <param name="aspect"></param>
        /// <param name="formatToString">自定义写入日志的内容</param>
        /// <param name="level"></param>
        /// <param name="logger"></param>
        /// <returns></returns>
        public static Aspect LogTimeCost(this Aspect aspect,
            Func<IAspectContext, Stopwatch, DateTime, DateTime, Exception, string> formatToString,
            LogLevel level, ILogger logger = null)
        {
            aspect.LoadCallerInfo();
            return aspect.TimeCost((context, stopwatch, dteStart, dteStop, exc) =>
            {
                var logger0 = logger ?? GetLogger(context);
                string message = null;
                if (formatToString != null) message = formatToString(context, stopwatch, dteStart, dteStop, exc);
                if (string.IsNullOrWhiteSpace(message))
                {
                    message = StringEx.SafeFormat("[{0}][Cost:{1:0.000}s][Start:{2:MMdd_HH:mm:ss}][Stop:{3:MMdd_HH:mm:ss}]",
                       context.CallerInfo?.GetCallerString(false),
                       stopwatch.ElapsedMilliseconds / 1000.0,
                       dteStart,
                       dteStop);
                }
                if (exc != null)
                {
                    message += StringEx.SafeFormat("{0}[Error:{1}:{2}]",
                        message, exc.GetType().FullName, exc.Message);
                }
                switch (level)
                {
                    case LogLevel.Debug: logger0.D(exc, message); break;
                    case LogLevel.Info: logger0.I(exc, message); break;
                    case LogLevel.Warn: logger0.W(exc, message); break;
                    case LogLevel.Error: logger0.E(exc, message); break;
                    case LogLevel.Fatal: logger0.F(exc, message); break;
                }
            });
        }

        #endregion
    }
}
